package com.owc.service.impl.app;

import com.alibaba.fastjson.JSON;
import com.owc.common.enums.ExceptionEnum;
import com.owc.common.exception.BadRequestException;
import com.owc.common.redis.CacheBuildKey;
import com.owc.common.redis.RedisUtil;
import com.owc.common.tool.*;
import com.owc.dao.TokenBalanceMapper;
import com.owc.dao.UserMapper;
import com.owc.dao.UserWalletAddressMapper;
import com.owc.domain.WalletAddress;
import com.owc.domain.entity.TokenBalance;
import com.owc.domain.entity.User;
import com.owc.service.WalletServer;
import com.owc.service.app.UserService;
import com.owc.service.app.WalletService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private WalletService walletService;
    @Resource
    private UserMapper userMapper;
    @Autowired
    private TransactionService transactionService;
    @Autowired
    private WalletServer walletServer;
    @Resource
    private UserWalletAddressMapper userWalletAddressMapper;
    @Resource
    private TokenBalanceMapper tokenBalanceMapper;
    @Override
    public User selectUser() {
        return userMapper.selectByPrimaryKey(1L);
    }


    @Override
    public Optional<User> selectByUserName(@NonNull String username) {
        return Optional.ofNullable(userMapper.selectByName(username));
    }

    @Override
    public Optional<User> selectByPhone(@NonNull String phone) {
        return Optional.ofNullable(userMapper.selectByPhone(phone));
    }

    @Override
    public Optional<User> selectById(Long userId) {
        return Optional.ofNullable(userMapper.selectByPrimaryKey(userId));
    }

    @Override
    public boolean phoneIsRegister(@NonNull String phone) {
        Assert.isTrue(ValidationUtils.isMobile(phone),"Illegal phone number format");
        return selectByPhone(phone).isPresent();
    }

    @Override
    public boolean createUser(@NonNull String phone, @NonNull String password) {

        WalletAddress walletAddress = buildWallet();

        String[] fields = {"phone","password","registerTime","username","userLevel","walletAddress"};
        String[] values = {phone, CryptographUtils.MD5(password), DateUtils.formatYMDHMS(new Date()),
                "OWC_" + UtilsService.genRandom(),"1", JSON.toJSONString(walletAddress)};

        try {
            //Instantiating the User model
            User user = BeanUtils.buildDefaultAttrModel(fields,values,User.class);
            transactionService.initUserInfo(user);
        } catch (Exception e) {
            log.error("Create new user failed. throw error:[{}]",e.getMessage());
            throw new BadRequestException("Create user info. errMsg:[{" + e.getMessage() + "}]");
        }
        return true;
    }

    @Override
    public boolean checkPayPassword(@NonNull Long userId, @NonNull String payPassword) {
        AtomicBoolean isOk = new AtomicBoolean(false);
        Optional.ofNullable(userMapper.selectByPrimaryKey(userId))
                .ifPresent(user -> isOk.set(user.getPayPassword().equals(CryptographUtils.MD5(payPassword))));
        return isOk.get();
    }

    @Override
    public void updatePayPassword(Long currentUserId, String password) {
        String[] fields = {"id","payPassword"};
        updateUserPassword(currentUserId, password, fields);
    }

    @Override
    public void updateLoginPassword(Long currentUserId, String password) {
        String[] fields = {"id","password"};
        updateUserPassword(currentUserId, password, fields);
    }

    @Override
    public String getPayPassword(Long userId) {
        User user = userMapper.selectByPrimaryKey(userId);
        Assert.notNull(user,ExceptionEnum.USER_INVALID_ERR.getMessage());
        return user.getPayPassword();
    }

    @Override
    public Optional<User> selectByEthAddress(@NonNull String address) {
        Long userId = userWalletAddressMapper.selectByEthAddress(address);
        if(userId == null){
            return Optional.empty();
        }
        return selectById(userId);
    }

    @Override
    public BigDecimal selectBalanceByUserId(Long userId,Long tokenId) {
        TokenBalance tokenBalance = tokenBalanceMapper.selectByUserIdAndTokenId(userId,tokenId);
        if(tokenBalance == null) return BigDecimal.ZERO;
        return tokenBalance.getBalance();
    }

    @Override
    public void cacheUserJwtToken(Long userId, String token) {
        RedisUtil._REDIS_MODULE.set(CacheBuildKey.buildUserJwtToken(userId),token);
    }

    @Override
    public String getUserJwtToken(Long userId) {
        return RedisUtil._REDIS_MODULE.get(CacheBuildKey.buildUserJwtToken(userId));
    }

    private void updateUserPassword(Long currentUserId, String password, String[] fields) {
        Object[] values = {currentUserId, CryptographUtils.MD5(password)};
        try {
            //Instantiating the User model
            User user = BeanUtils.buildDefaultAttrModel(fields,values,User.class);
            userMapper.updateByPrimaryKeySelective(user);
        } catch (Exception e) {
            log.error("Modify user password failed. throw error:[{}]",e.getMessage());
            throw new BadRequestException(ExceptionEnum.UNKNOWN_ERR);
        }
    }

    private WalletAddress buildWallet() {
        WalletAddress walletAddress = new WalletAddress();
        try {
            walletAddress.setEth(walletServer.createETHWalletAddress());
            walletAddress.setBtc(walletServer.createBTCWalletAddress());
        } catch (Exception e) {
            log.error("Generate wallet address failed. throw message:[{}]",e.getMessage());
            throw new BadRequestException("Wallet server linker failed.");
        }
        return walletAddress;
    }
}
